/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package footprintscrollbar;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.LinkedList;
import javax.swing.JComponent;
import javax.swing.JScrollBar;
import javax.swing.plaf.basic.BasicScrollBarUI;

/**
 *
 * @author mehlicious, karsonbraaten
 */
public class FootprintScrollBar extends JScrollBar{

    public int THUMB_HEIGHT = 16;
    private int THUMB_WIDTH = 16;
    private int FOOTPRINT_WIDTH = THUMB_WIDTH;
    private int FOOTPRINT_HEIGHT = THUMB_HEIGHT / 2;
    private int LARGE_INT = 1000000;
    private int SMALL_INT = -1000000;
    
    public char TO_NEXT = 'n';
    public char TO_PREV = 'p';
    public char TO_DELETE = 'd';
    public char TO_ADD = 'r';
    
    private int _extent;
    private int _maxVal;
    private int _actualMax;
    private int _half;
    
    boolean FOOTPRINT_CLICK = false;
    
    LinkedList<Point> footprints;
    
    private long currentTime;
    public long latency = 2000; // <=> 5 seconds
    
    private FootprintScrollBar _fpsb = this;
    
    public FootprintScrollBar() {
        super();
        // Sets the thumb size to be 2 pixels
        // The override isn't needed anymore (yet anyway)
        
        currentTime = System.currentTimeMillis();
        footprints = new LinkedList<Point>();
        this.setUI(new FootprintScrollbarUI());
        
        
        this.addAdjustmentListener(new FPAdjustmentListener());
        this.addKeyListener(new FPKeyListener());
        this.addMouseListener(new FPMouseListener());
    
    }
    
    public void updateFootprint(){
        addFootprintAuto();
        
        _extent = this.getModel().getExtent();
        _maxVal = this.getMaximum();
        _actualMax = _maxVal - _extent;        
        _half = _maxVal/2;
        
        this.requestFocus();
        //System.out.println("extent: " + _extent);
        //System.out.println("axtualMax: " + _actualMax);
    }
    
    private void addFootprintAuto(){
        Long d = System.currentTimeMillis();
        
        Long diff = d - currentTime;
        
        currentTime = d;
        
        //System.out.println("DIFF: " + diff);
        
        if(diff > latency) {
            addFootprint();     
        }
        
    }
    
    public void addFootprint(){        
        // If the footprint doesn't already exist, add it
        if(!footprints.contains(new Point(this.getValue(), this.getHeight()))){
            footprints.add(new Point(this.getValue(), this.getHeight()));
        }
        
        for (Point f : footprints) {
            System.out.println("(" + f.x + ", " + f.y + ")");
        }
    }
    
    public void removeFootprint(int location){
        footprints.removeFirstOccurrence(new Point(this.getValue(), this.getHeight()));
        if (footprints.isEmpty()) System.out.println("There are no footprints");
    }
    
    public void goToNextFootprint() {
        if (footprints.isEmpty()) { return; }
        
        Point next = new Point(LARGE_INT, LARGE_INT);
        for (Point f : footprints) {
            // Next point is the prev point greater than current scroll location
            if (f.x - this.getValue() > 0 && f.x - this.getValue() < next.x - this.getValue()) {
                next.x = f.x;
                next.y = f.y;
            }
        }
        
        // If there is no point below the scrollbar check for one closest to the top
        // next.x == LARGE_INT means next never changed so there is no point
        if (next.x == LARGE_INT) {
            Point first = new Point(LARGE_INT, LARGE_INT);
            for (Point f : footprints) {
                System.out.print("(" + f.x + ", " + f.y + ") ");
                System.out.println(first.x - this.getValue());
                if (f.x < first.x - this.getValue()) {
                    first.x = f.x;
                    first.y = f.y;
                    if (f.x == 0) break; // 0 will be first point at top
                }
            }
            this.setValue(first.x);
        } else {
            this.setValue(next.x); 
        }
    }
    
    public void goToPreviousFootprint() {
        if (footprints.isEmpty()) { return; }
        
        Point prev = new Point(SMALL_INT, SMALL_INT);
        for (Point f : footprints) {
            //System.out.println("this.getValue() - f.x = " + (this.getValue() - f.x));
            if (this.getValue() - f.x > 0 && this.getValue() - f.x < this.getValue() - prev.x) {
                System.out.println("Setting previous value");
                prev.x = f.x;
                prev.y = f.y;
            }
        }
                
        // If there is no point above the scrollbar check for one closest to the bottom
        if (prev.x == SMALL_INT) {
            Point last = new Point(SMALL_INT, SMALL_INT);
            for (Point f : footprints) {
                if (f.x > last.x - this.getValue()) {
                    last.x = f.x;
                    last.y = f.y;
                }
            }
            this.setValue(last.x);
        } else {
            this.setValue(prev.x); 
        }
    }
    
    public void goToFootprint(int x, int y) {
        //System.out.println("goToFootprint() called");
        //System.out.println("X: " + x + "\tY: " + y);
        //System.out.println(this.toString());
        for (Point f : footprints) {
            int compX = translateValueToPixel(f.x);
            
            if(y > compX && y < compX + THUMB_HEIGHT){
                this.setValue(f.x);
                break;
            }
        }
    }
    
    public int translateValueToPixel(int x){
        double drawAt = (((double)x/(double)_actualMax)*(double)_extent);
        double toAdd;

        if(x < _half) {
            toAdd = ((double)x/(double)_half)*(double)THUMB_HEIGHT;
            toAdd = THUMB_HEIGHT - toAdd;
            drawAt += toAdd;
        }
        else{
            toAdd = ((double)x/(double)_actualMax)*((double)THUMB_HEIGHT*1.5);
            drawAt -= toAdd;
            //System.out.println("drawAt: " + drawAt);
        }
        
        return (int)drawAt;
        
    }
    
    public class FootprintScrollbarUI extends BasicScrollBarUI{
        @Override
        protected void paintTrack(Graphics g, JComponent c, Rectangle trackbounds){
            super.paintTrack(g, c, trackbounds);
            
            //System.out.println(thumbRect.toString());
            
            maximumThumbSize = new Dimension(THUMB_WIDTH,THUMB_HEIGHT);
            minimumThumbSize = new Dimension(THUMB_WIDTH,THUMB_HEIGHT);
            thumbRect = new Rectangle(thumbRect.x, thumbRect.y, THUMB_WIDTH,THUMB_HEIGHT);
            
            for (Point f : footprints){
                g.setColor(Color.yellow);
                double drawAt = (((double)f.x/(double)_actualMax)*(double)_extent);
                double toAdd;
                
                if(f.x < _half) {
                    toAdd = ((double)f.x/(double)_half)*(double)THUMB_HEIGHT;
                    toAdd = THUMB_HEIGHT - toAdd;
                    drawAt += toAdd;
                }
                else{
                    toAdd = ((double)f.x/(double)_actualMax)*((double)THUMB_HEIGHT*1.5);
                    drawAt -= toAdd;
                    //System.out.println("drawAt: " + drawAt);
                }
                //System.out.println("DRAWING AT: " + (int)drawAt);
                g.fillRect(0, (int)drawAt, FOOTPRINT_WIDTH, FOOTPRINT_HEIGHT);                         
            }
        }

//        @Override
//        public void paintThumb(final Graphics g, final JComponent c, final Rectangle thumbBounds) {
//            thumbBounds.setSize(THUMB_WIDTH, THUMB_HEIGHT);
//            System.out.println("thumbounds.y = " + thumbBounds.y);
//            super.paintThumb(g, c, thumbBounds);
//            if (thumbBounds.isEmpty() || !this.scrollbar.isEnabled()) { return; }
//                System.out.println("thumbounds.y = " + thumbBounds.y);
//                //setThumbBounds(thumbBounds.x,thumbBounds.y,THUMB_WIDTH,THUMB_HEIGHT);
//                
//                g.translate(thumbBounds.x, thumbBounds.y);
//                g.setColor(this.thumbDarkShadowColor); 
//                g.drawRect(0, 0, THUMB_WIDTH, THUMB_HEIGHT); 
//                g.setColor(this.thumbColor); 
//                g.fillRect(0, 0, THUMB_WIDTH, THUMB_HEIGHT); 
//                g.setColor(this.thumbHighlightColor); 
//                g.setColor(this.thumbLightShadowColor);
//                g.translate(-thumbBounds.x, -thumbBounds.y);
//        }        
    }
    
    class FPAdjustmentListener implements AdjustmentListener{
        @Override
        public void adjustmentValueChanged(AdjustmentEvent ae) {
            //System.out.println("VALUE: " + _fpsb.getValue());
            //System.out.println("MAXVAL: " + scrollbar.getMaximum());
            //System.out.println("EXTENT: " + scrollbar.getModel().getExtent());
            //System.out.println("MODELVAL: " + scrollbar.getModel().getValue());
            _fpsb.updateFootprint();
            // Gets rid of past draws of rectangles
            _fpsb.repaint();
        }   
    }
    
    class FPKeyListener implements KeyListener{

        @Override
        public void keyTyped(KeyEvent k) {
            System.out.println("Key is pressed: " + k.getKeyChar());
            if (k.getKeyChar() == TO_ADD) { _fpsb.addFootprint(); }
            if (k.getKeyChar() == TO_NEXT) { _fpsb.goToNextFootprint(); }
            if (k.getKeyChar() == TO_PREV) { _fpsb.goToPreviousFootprint(); }
            if (k.getKeyChar() == TO_DELETE) { _fpsb.removeFootprint(_fpsb.getValue()); }
            
            _fpsb.repaint();
        }

        @Override
        public void keyPressed(KeyEvent k) { if (k.getKeyChar() == 'b') FOOTPRINT_CLICK = true; }

        @Override
        public void keyReleased(KeyEvent k) { FOOTPRINT_CLICK = false; }
    }
    
    class FPMouseListener implements MouseListener{
        @Override
        public void mouseClicked(MouseEvent e) { _fpsb.goToFootprint(e.getX(), e.getY()); }

        // Do not need to implement
        @Override
        public void mousePressed(MouseEvent me) { }

        @Override
        public void mouseReleased(MouseEvent me) { }

        @Override
        public void mouseEntered(MouseEvent me) { }

        @Override
        public void mouseExited(MouseEvent me) { } 
    }    
}
